package com.dd.stack.jvm.classloader;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/9/12 9:31
 * @Description 类功能作用说明
 */
import java.io.*;

/**
 * 自定义类加载器示例
 * 重写findClass方法，遵循双亲委派模型
 */
public class MyClassLoader extends ClassLoader {
    private final String classPath; // 类加载的路径

    public MyClassLoader(String classPath) {
        // 默认父加载器为系统类加载器
        this.classPath = classPath;
    }

    public MyClassLoader(String classPath, ClassLoader parent) {
        super(parent); // 显式指定父加载器
        this.classPath = classPath;
    }

    /**
     * 重写findClass方法，这是打破双亲委派的推荐方式
     * 1. 首先让父加载器尝试加载（委派）
     * 2. 如果父加载器找不到，再自己加载
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("MyClassLoader attempting to find class: " + name);

        // 1. 尝试从自定义路径加载字节码
        byte[] classData = loadClassData(name);
        if (classData != null) {
            System.out.println("Class found by MyClassLoader, defining: " + name);
            // 将字节数组转换为Class对象
            return defineClass(name, classData, 0, classData.length);
        }

        // 2. 如果自定义路径找不到，抛出ClassNotFoundException
        // 这会触发父类的loadClass方法继续向上委派
        System.out.println("Class not found by MyClassLoader, delegating to parent");
        throw new ClassNotFoundException();
    }

    /**
     * 从指定路径加载类的字节码
     */
    private byte[] loadClassData(String className) {
        // 将包名转换为文件路径
        String path = className.replace('.', '/') + ".class";
        File file = new File(classPath, path);

        if (!file.exists()) {
            return null;
        }

        try (InputStream is = new FileInputStream(file);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }

            return baos.toByteArray();
        } catch (IOException e) {
            return null;
        }
    }
}

// 使用示例
class MyClassLoaderUseDemo {
    public static void main(String[] args) throws Exception {
        // 创建自定义类加载器，指定加载路径
        MyClassLoader myLoader = new MyClassLoader("E:\\test", MyClassLoaderUseDemo.class.getClassLoader());

        // 要加载的类名
        String className = "com.dd.stack.jvm.classloader.test.TestClass";

        try {
            System.out.println("=== 开始加载类 ===");

            // 加载类 - 这会触发双亲委派机制
            Class<?> loadedClass = myLoader.loadClass(className);

            System.out.println("=== 类加载完成 ===");
            System.out.println("类加载器: " + loadedClass.getClassLoader());
            System.out.println("类名称: " + loadedClass.getName());

            // 创建实例并调用方法
            Object instance = loadedClass.newInstance();
            System.out.println("实例创建成功: " + instance);

        } catch (ClassNotFoundException e) {
            System.out.println("类未找到: " + className);
        }

        /*
            === 开始加载类 ===
            === 类加载完成 ===
            类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
            类名称: com.dd.stack.jvm.classloader.test.TestClass
            TestClass initialized by: sun.misc.Launcher$AppClassLoader@18b4aac2
            实例创建成功: com.dd.stack.jvm.classloader.test.TestClass@6e0be858

            双亲委派机制的作用过程：
                委派阶段：当调用 myLoader.loadClass("com.example.TestClass") 时：
                    首先检查该类是否已被加载
                    未加载则调用父类（系统类加载器）的 loadClass 方法
                    系统类加载器继续委派给扩展类加载器
                    扩展类加载器委派给启动类加载器
                查找阶段：
                    启动类加载器在核心库中查找 → 未找到
                    扩展类加载器在扩展目录中查找 → 未找到
                    系统类加载器在classpath中查找 → 未找到（因为我们的类在自定义路径）
                自定义加载阶段：
                    所有父加载器都找不到，最终调用 MyClassLoader.findClass()
                    自定义类加载器从指定路径加载字节码
                    调用 defineClass() 完成类的定义

            输出示例：
                text
                === 开始加载类 ===
                MyClassLoader attempting to find class: com.example.TestClass
                Class found by MyClassLoader, defining: com.example.TestClass
                === 类加载完成 ===
                类加载器: MyClassLoader@xxxxxx
                类名称: com.example.TestClass
                TestClass initialized by: MyClassLoader@xxxxxx
                实例创建成功: com.example.TestClass@yyyyyy

            关键点：
                自定义类加载器遵循双亲委派模型
                只有在所有父加载器都无法加载时，才自己加载
                通过重写 findClass() 而不是 loadClass() 来保持双亲委派机制
                适合加载非classpath下的类或实现热部署等功能
         */
    }
}